-- SimpleEncryptM
-- created by panyl
-- 公共接口模块

module("SimpleEncryptM", package.seeall);

local dungeon_monitor;

local coding = {};

local itemStat = {};
local petStat  = {};
local achievementStat = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    coding = {};
    local attrib_coding_CSV = get_attrib_coding_CSV();
    for i = 1, #attrib_coding_CSV do
        local row = attrib_coding_CSV[i];

        coding[row.attrib] = row;
    end
end

-- 模块初始化操作
function init()
    itemStat = {};
    petStat  = {};

    loadCsv();
    EventMgr.removeAll("SimpleEncryptM");
    EventMgr.register("SimpleEncryptM", event.ITEM_COUNT_UPDATED, whenItemUpdated);
    EventMgr.register("SimpleEncryptM", event.GAIN_PET, whenGainPet);
    EventMgr.register("SimpleEncryptM", event.UPDATE_ACHIEVEMENT, whenUpdateAchievement);

    -- 一个定时事件实时监控着迷宫内的属性，3秒检查一次
    ScheduleM.createScheme("dungeon_monitor", dungeon_monitor, 3, true)
end

-- 监控迷宫内属性
dungeon_monitor = function()
    if DUNGEON_GAMING ~= DungeonM.getState() then
        -- 不是在迷宫内？暂时不管了
        return;
    end

    -- 验证客户端就不管了
    if isVerifyClient() then
        return;
    end

    -- 定死的，不能超出这么多
    local attrib_limit = {
        ["attack"] = 100,
        ["magic"]  = 100,
        ["hp"]     = 2000,
        ["mp"]     = 1000,
        ["search_point"] = 2000,
    };

    local err = false;
    local base_map = {};
    local code_map = {};
    for attrib, value in pairs(attrib_limit) do
        local curValue = ME.user.dbase:query(attrib, 0);
        local codeValue = queryCodeAttrib(attrib);

        -- 如果超出范围
        if codeValue and curValue > codeValue + value then
            --err = true;--超出范围也不管！
        end

        base_map[attrib] = curValue;
        code_map[attrib] = codeValue;
    end
	return;--这里不管有没异常，都返回
    -- 如果没有异常
    if not err then
        return;
    end

    -- 如果有异常，直接发给服务器'
    local v = {["base_attrib"] = base_map, ["code_attrib"] = code_map, };
    Communicate.send("CMD_SUBMIT_ATTRIB_ERROR", {["extra"] = v, });

    -- 强制关闭
    EventMgr.fire(event.FORCE_SHUTDOWN, {["reason"] = getLocStr("attrib_error_shutdown")});
end

function query(attrib, path)
    local m = coding[attrib];

    if not m or not path then
        return m;
    end

    return m[path];
end

function whenGainPet(classId)
    if DungeonM.getState() ~= DUNGEON_GAMING then
        return;
    end

    petStat[classId] = 1;
end

function whenUpdateAchievement(para)
    local id = para.id;
    local progress = para.progress;

    achievementStat[id] = progress;
end

function whenItemUpdated(classId)
    if DungeonM.getState() ~= DUNGEON_GAMING then
        return;
    end

    itemStat[classId] = ItemM.getAmount(ME.user, classId);
end

-- 加密基本属性
function attribCoding(user)
    user.dbase:delete("attrib_coding");

    for attrib, _ in pairs(coding) do
        local base = user.dbase:query(attrib);
        if base then
            user:attribCoding(attrib, base);
        end
    end
end

-- 收集加密属性
function collectAttribCoding(user)
    local ret = table.deepcopy(ME.user.dbase:query("attrib_coding", {}));

    -- 把迷宫内属性也弄进来，这个不加密了
    ret["dungeon_attrib"] = table.deepcopy(user.dbase:query("dungeon_attrib"));

    local currItems = {__mapping = 1, };
    for classId, _ in pairs(itemStat) do
        currItems[classId] = ItemM.getAmount(user, classId);
    end
    ret["items"] = currItems;

    ret["pets"] = to_mapping(petStat);

    ret["achievements"] = to_mapping(achievementStat);

    achievementStat = {};

    -- 清空
    itemStat = {};
    petStat  = {};

    ret["common_rand_seed"] = user.dbase:query("common_rand_seed");

    -- 召唤兽
    ret["summon"] = to_mapping(ME.user.dbase:query("summon"));

    -- 魔藤
    local bine = ME.user.bine;
    if bine then
        ret["bine"] = bine:getSaveData();
    end

    -- 阿瓦隆巨炮
    local avalonCannon = ME.user.avalon_cannon;
    if avalonCannon then
        ret["avalon_cannon"] = avalonCannon:getSaveData();
    end

    -- 装备
    ret["equipments"] = to_mapping(ME.user.equipments);

    -- 称号
    ret["career"] = CareerM.packCareerData(ME.user:getActivePet():getCareerData());

    return table.deepcopy(ret);
end

function code(attrib, amount)
    local code = SimpleEncryptM.query(attrib, "code");
    local tail = SimpleEncryptM.query(attrib, "tail");
    local flag = 0;

    if amount < 0 then
        flag = 1;
        amount = -amount;
    end

    -- 异或之后存放到高位（20bit）
    local v = bit.bxor(amount, code);
    v = bit.blshift(v, 12);

    return v + tail * 2 + flag;
end

function decode(attrib, v)
    local code = SimpleEncryptM.query(attrib, "code");
    local tail = SimpleEncryptM.query(attrib, "tail");
    local flag = v % 2;

    -- 高位逻辑右移12位，并异或解密
    v = bit.blogic_rshift(v, 12);
    v = bit.bxor(v, code);

    -- 负数
    if flag == 1 then
        v = -v;
    end

    return v;
end

function queryCodeAttrib(attrib)
    local info = ME.user.dbase:query("attrib_coding", {});
    if not info[attrib] then
        return;
    end

    return decode(attrib, info[attrib]);
end
